import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
const SUPABASE_URL = "https://mhvphjcreqhfnulflgiy.supabase.co";
const SUPABASE_KEY = "sb_publishable_CPulXF1pmXf_7RacRrv1ag_MHuGAxXC";
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
const genres = ["All","Werewolf","Mafia","Erotica","LGBTQ","Billionaire Drama","Vampire","Paranormal","Christian Romance"];
let books = [];
let selectedGenre = "All";
let currentBook = null;
let chapters = [];
let currentChapterIndex = 0;
let reader = null;
let isRegister = false;
let libraryIds = new Set();
const unlockedChapterIds = new Set();
const $ = id => document.getElementById(id);
function notify(message) {
const toast = $("toast");
if (!toast) return;
toast.textContent = message;
toast.style.display = "block";
clearTimeout(window.toastTimer);
window.toastTimer = setTimeout(() => toast.style.display = "none", 3500);
}
function escapeHTML(value = "") {
return String(value).replace(/[&<>"']/g, c => ({
"&":"&","<":"<",">":">","\"":""","'":"'"
})[c]);
}
function showPage(name) {
if (name === "profile" || name === "account") name = reader ? "profile" : "account";
const page = $("page-" + name);
if (!page) return;
document.querySelectorAll(".page").forEach(p => p.classList.remove("active"));
page.classList.add("active");
document.querySelectorAll("[data-page]").forEach(b => b.classList.toggle("active", b.dataset.page === name));
window.scrollTo({ top: 0, behavior: "smooth" });
if (name === "profile" && reader) loadProfile();
if (name === "notifications") loadNotifications();
if (name === "wallet" && reader) loadTransactions();
}
function renderGenres() {
["home-genres","browse-genres"].forEach(id => {
const el = $(id);
if (!el) return;
el.innerHTML = genres.map(g =>
``
).join("");
});
}
function bookCard(book) {
const id = escapeHTML(book.id);
const title = escapeHTML(book.title || "Untitled story");
const author = escapeHTML(book.author || book.author_name || "Fictional World");
const genre = escapeHTML(book.genre || "Fiction");
const cover = book.cover_url || book.cover_image || "";
return `
${cover ? `
})
` : ""}
${title}
${title}
${author}
${genre}
`;
}
function renderBookList(target, list) {
const el = $(target);
if (!el) return;
el.innerHTML = list.length ? list.map(bookCard).join("") : `
No stories to display yet.
`;
}
function filteredBooks() {
const q = ($("search-input")?.value || "").trim().toLowerCase();
return books.filter(b => {
const genreMatch = selectedGenre === "All" || String(b.genre || "").toLowerCase() === selectedGenre.toLowerCase();
const searchMatch = !q || [b.title, b.author, b.author_name, b.genre].some(v => String(v || "").toLowerCase().includes(q));
return genreMatch && searchMatch;
});
}
function renderBooks() {
const featured = books.filter(b => b.featured === true);
renderBookList("featured-books", featured.slice(0, 5).concat(books.filter(b => !b.featured).slice(0, Math.max(0, 5 - featured.length))));
renderBookList("ongoing-books", books.filter(b => /ongoing/i.test(b.status || "")).slice(0, 5));
renderBookList("completed-books", books.filter(b => /completed/i.test(b.status || "")).slice(0, 5));
const list = filteredBooks();
renderBookList("all-books", list);
const total = $("book-total");
if (total) total.textContent = `${list.length} stories`;
const heading = $("browse-heading");
if (heading) heading.textContent = selectedGenre === "All" ? "All stories" : selectedGenre;
renderGenres();
}
async function loadBooks() {
const allBooks = $("all-books");
if (allBooks) allBooks.innerHTML = `Loading stories...
`;
const { data, error } = await supabase.from("books").select("*").limit(100);
if (error) {
console.error("Books:", error.message);
if (allBooks) allBooks.innerHTML = `Stories could not be loaded. Please refresh and try again.
`;
notify("Unable to load stories. Please try again.");
return;
}
books = data || [];
renderBooks();
}function renderPackages() {
const packages = [
{ id: "coins_100", coins: 100, price: "$2.99" },
{ id: "coins_300", coins: 300, price: "$7.99" },
{ id: "coins_600", coins: 600, price: "$14.99" },
{ id: "coins_1200", coins: 1200, price: "$27.99" }
];
const el = $("coin-packages");
if (el) el.innerHTML = packages.map(p => `
Coin package
${p.coins.toLocaleString()} coins
${p.price}
`
).join("");
const plans = [
{ id: "readers_pass", name: "Readers Pass", monthly: "$5.99", yearly: "$49.99", benefits: ["No banner ad","50 free coins monthly","5% off on coin purchases"] },
{ id: "vip_reader", name: "VIP Reader", monthly: "$9.99", yearly: "$89.99", benefits: ["No banner ad","150 free coins monthly","9% off on coin purchases"] },
{ id: "exclusive_reader", name: "Exclusive Reader", monthly: "$19.99", yearly: "$179.99", benefits: ["No banner ad","300 free coins monthly","15% off on coin purchases","2 free chapter unlocks"] }
];
const membershipEl = $("membership-plans");
if (membershipEl) membershipEl.innerHTML = plans.map((p, i) => `
${i === 1 ? "Popular choice" : "Reader membership"}
${p.name}
${p.monthly} / month
${p.yearly} when billed yearly
${p.benefits.map(x => `- ${x}
`).join("")}
`
).join("");
}
function updateAccountUI() {
$("login-nav").textContent = "My Account";
if (!reader) {
$("profile-email").textContent = "Sign in to view your profile";
$("profile-name").textContent = "Reader";
$("profile-reader-id").textContent = "—";
$("profile-gender").textContent = "Not provided";
$("profile-avatar").textContent = "♡";
return;
}
$("profile-email").textContent = reader.email || "";
$("profile-name").textContent = reader.user_metadata?.display_name || reader.email?.split("@")[0] || "Reader";
}
async function getSession() {
try {
const { data: { session }, error } = await supabase.auth.getSession();
if (error) {
console.error("Session error:", error);
reader = null;
return;
}
reader = session?.user || null;
if (!reader) {
updateAccountUI();
$("coin-count").textContent = "0";
$("wallet-coins").textContent = "0";
$("profile-coins").textContent = "0";
return;
}
await loadProfile();
await loadLibrary();
await loadTransactions();
await loadNotifications();
if ($("page-account") && $("page-account").classList.contains("active")) showPage("profile");
} catch (error) {
console.error("Could not get session:", error);
reader = null;
}
}
async function loadProfile() {
if (!reader) return;
const { data, error } = await supabase.from("users").select("*").eq("auth_user_id", reader.id).maybeSingle();
if (error) console.warn("Profile load error:", error.message);
const profile = data || {};
const name = profile.name || reader.user_metadata?.display_name || reader.email?.split("@")[0] || "Reader";
const avatar = profile.avatar_url || profile.profile_picture || profile.photo_url || reader.user_metadata?.avatar_url || reader.user_metadata?.picture || "";
const gender = profile.gender || reader.user_metadata?.gender || "Not provided";
const coins = Number(profile.coin_balance ?? 0);
$("coin-count").textContent = coins;
$("wallet-coins").textContent = coins;
$("profile-coins").textContent = coins;
$("profile-display-name").value = name;
$("profile-name").textContent = name;
$("profile-email").textContent = reader.email || profile.email || "Email not available";
$("profile-reader-id").textContent = profile.id ?? reader.id;
$("profile-gender").textContent = gender;
$("profile-tier").textContent = profile.subscription_tier || "Free";
$("profile-tier-stat").textContent = profile.subscription_tier || "Free";
$("profile-avatar").innerHTML = avatar
? `
`
: "♡";
}
async function loadLibrary() {
if (!reader) {
$("library-note").hidden = false;
renderBookList("library-books", []);
return;
}
const { data, error } = await supabase.from("library").select("*").eq("user_id", reader.id);
if (error) {
console.warn("Library load error:", error.message);
return;
}
libraryIds = new Set((data || []).map(x => String(x.book_id)));
$("profile-saved").textContent = libraryIds.size;
$("library-note").hidden = libraryIds.size > 0;
renderBookList("library-books", books.filter(b => libraryIds.has(String(b.id))));
}
async function loadTransactions() {
if (!reader) return;
const { data, error } = await supabase.from("transactions").select("*").eq("user_id", reader.id).order("created_at", { ascending: false }).limit(20);
if (error) {
console.warn("Transactions load error:", error.message);
$("transactions").innerHTML = `Transaction history is unavailable right now.
`;
return;
}
if (!data || data.length === 0) {
$("transactions").innerHTML = `No transactions yet.
`;
return;
}
$("transactions").innerHTML = data.map(t => `
${t.payment_status || "Transaction"} · ${t.amount_ngn ?? 0} NGN
${t.coins_added ?? 0} coins
`).join("");
}
async function loadNotifications() {
const list = $("notification-list");
if (!list) return;
if (!reader) {
list.innerHTML = `Log in to view your notifications.
`;
return;
}
const { data, error } = await supabase.from("notification").select("*").eq("user_id", reader.id).order("created_at", { ascending: false }).limit(30);
if (error) {
console.warn("Notifications load error:", error.message);
list.innerHTML = `Notifications are unavailable right now.
`;
return;
}
if (!data || data.length === 0) {
list.innerHTML = `You're all caught up. No notifications are available.
`;
return;
}
list.innerHTML = data.map(n => `
${escapeHTML(n.title || "Notification")}
${escapeHTML(n.message || n.body || "")}
${new Date(n.created_at).toLocaleString()}
${escapeHTML(n.type || "info")}
`).join("");
}